#include "dma.hpp"
#include "hc32_ddl.h"
#include "semaphore.hpp"

///> 设备类
using namespace device;

#define DMA_IRQ_NUM		5

typedef struct
{
		volatile uint32_t SAR;
    volatile uint32_t DAR;
    union
    {
        volatile uint32_t DTCTL;
        stc_dma_dtctl_field_t DTCTL_f;
    };
    union
    {
        volatile uint32_t RPT;
        stc_dma_rpt_field_t RPT_f;
        volatile uint32_t RPTB;
        stc_dma_rptb_field_t RPTB_f;
    };
    union
    {
        volatile uint32_t SNSEQCTL;
        stc_dma_snseqctl_field_t SNSEQCTL_f;
        volatile uint32_t SNSEQCTLB;
        stc_dma_snseqctlb_field_t SNSEQCTLB_f;
    };
    union
    {
        volatile uint32_t DNSEQCTL;
        stc_dma_dnseqctl_field_t DNSEQCTL_f;
        volatile uint32_t DNSEQCTLB;
        stc_dma_dnseqctlb_field_t DNSEQCTLB_f;
    };
    union
    {
        volatile uint32_t LLP;
        stc_dma_llp_field_t LLP_f;
    };
    union
    {
        volatile uint32_t CHCTL;
        stc_dma_chctl_field_t CHCTL_f;
    };
    volatile uint32_t MONSAR;
    volatile uint32_t MONDAR;
    union
    {
        volatile uint32_t MONDTCTL;
        stc_dma_mondtctl_field_t MONDTCTL_f;
    };
    union
    {
        volatile uint32_t MONRPT;
        stc_dma_monrpt_field_t MONRPT_f;
    };
    union
    {
        volatile uint32_t MONSNSEQCTL;
        stc_dma_monsnseqctl_field_t MONSNSEQCTL_f;
    };
    union
    {
        volatile uint32_t MONDNSEQCTL;
        stc_dma_mondnseqctl_field_t MONDNSEQCTL_f;
    };
}DMAChannelTPDF;

typedef struct
{
	bool bUsed;
	bool bCircularMode;
	dma::DmaTransModeTPDF tDmaTransMode;
	uint8_t ucEnIndex;
	uint16_t tIrqId;
	M4_DMA_TypeDef *tDma;
	DMAChannelTPDF *tDmaChannel;
	uint32_t *uiAos;
	dma::DmaCompleteDelegate tDmaCompleteCallback;
}DmaMapTPDF;

static DmaMapTPDF mDmaMap[] = 
{
	{false, false,	dma::DMA_TRANS_MODE_SFR_TO_MEM,	0,	INT_DMA1_TC0,	M4_DMA1,	(DMAChannelTPDF *)&M4_DMA1->SAR0,	(uint32_t *)&M4_AOS->DMA1_TRGSEL0,	0},
	{false, false,	dma::DMA_TRANS_MODE_SFR_TO_MEM,	1,	INT_DMA1_TC1,	M4_DMA1,	(DMAChannelTPDF *)&M4_DMA1->SAR1,	(uint32_t *)&M4_AOS->DMA1_TRGSEL1,	0},
	{false, false,	dma::DMA_TRANS_MODE_SFR_TO_MEM,	2,	INT_DMA1_TC2,	M4_DMA1,	(DMAChannelTPDF *)&M4_DMA1->SAR2,	(uint32_t *)&M4_AOS->DMA1_TRGSEL2,	0},
	{false, false,	dma::DMA_TRANS_MODE_SFR_TO_MEM,	3,	INT_DMA1_TC3,	M4_DMA1,	(DMAChannelTPDF *)&M4_DMA1->SAR3,	(uint32_t *)&M4_AOS->DMA1_TRGSEL3,	0},
	{false, false,	dma::DMA_TRANS_MODE_SFR_TO_MEM,	0,	INT_DMA2_TC0,	M4_DMA2,	(DMAChannelTPDF *)&M4_DMA2->SAR0,	(uint32_t *)&M4_AOS->DMA2_TRGSEL0,	0},
	{false, false,	dma::DMA_TRANS_MODE_SFR_TO_MEM,	1,	INT_DMA2_TC1,	M4_DMA2,	(DMAChannelTPDF *)&M4_DMA2->SAR1,	(uint32_t *)&M4_AOS->DMA2_TRGSEL1,	0},
	{false, false,	dma::DMA_TRANS_MODE_SFR_TO_MEM,	2,	INT_DMA2_TC2,	M4_DMA2,	(DMAChannelTPDF *)&M4_DMA2->SAR2,	(uint32_t *)&M4_AOS->DMA2_TRGSEL2,	0},
	{false, false,	dma::DMA_TRANS_MODE_SFR_TO_MEM,	3,	INT_DMA2_TC3,	M4_DMA2,	(DMAChannelTPDF *)&M4_DMA2->SAR3,	(uint32_t *)&M4_AOS->DMA2_TRGSEL3,	0},
};

static systemos::semaphore *mSemaphore = 0;

extern "C" void IRQ005_Handler(void)
{
	if(M4_DMA1->INTSTAT1 & 0x00010001)
	{
		M4_DMA1->INTCLR1 = 0x00010001;
		if(mDmaMap[0].tDmaCompleteCallback)
		{
			mDmaMap[0].tDmaCompleteCallback();
		}
	}
}

extern "C" void IRQ006_Handler(void)
{
	if(M4_DMA1->INTSTAT1 & 0x00020002)
	{
		M4_DMA1->INTCLR1 = 0x00020002;
		if(mDmaMap[1].tDmaCompleteCallback)
		{
			mDmaMap[1].tDmaCompleteCallback();
		}
	}
}

extern "C" void IRQ007_Handler(void)
{
	if(M4_DMA1->INTSTAT1 & 0x00040004)
	{
		M4_DMA1->INTCLR1 = 0x00040004;
		if(mDmaMap[2].tDmaCompleteCallback)
		{
			mDmaMap[2].tDmaCompleteCallback();
		}
	}
}

extern "C" void IRQ008_Handler(void)
{
	if(M4_DMA1->INTSTAT1 & 0x00080008)
	{
		M4_DMA1->INTCLR1 = 0x00080008;
		if(mDmaMap[3].tDmaCompleteCallback)
		{
			mDmaMap[3].tDmaCompleteCallback();
		}
	}
}

extern "C" void IRQ009_Handler(void)
{
	if(M4_DMA2->INTSTAT1 & 0x00010001)
	{
		M4_DMA2->INTCLR1 = 0x00010001;
		if(mDmaMap[4].tDmaCompleteCallback)
		{
			mDmaMap[4].tDmaCompleteCallback();
		}
	}
}

extern "C" void IRQ010_Handler(void)
{
	if(M4_DMA2->INTSTAT1 & 0x00020002)
	{
		M4_DMA2->INTCLR1 = 0x00020002;
		if(mDmaMap[5].tDmaCompleteCallback)
		{
			mDmaMap[5].tDmaCompleteCallback();
		}
	}
}

extern "C" void IRQ011_Handler(void)
{
	if(M4_DMA2->INTSTAT1 & 0x00040004)
	{
		M4_DMA2->INTCLR1 = 0x00040004;
		if(mDmaMap[6].tDmaCompleteCallback)
		{
			mDmaMap[6].tDmaCompleteCallback();
		}
	}
}

extern "C" void IRQ012_Handler(void)
{
	if(M4_DMA2->INTSTAT1 & 0x00080008)
	{
		M4_DMA2->INTCLR1 = 0x00080008;
		if(mDmaMap[7].tDmaCompleteCallback)
		{
			mDmaMap[7].tDmaCompleteCallback();
		}
	}
}

HandleTPDF dma::Request(DmaInitTPDF *tDmaInit)
{
	uint8_t i;
	
	if(!mSemaphore)
	{
		mSemaphore = new systemos::semaphore();
	}
	mSemaphore->Take(portMAX_DELAY);
	for(i = 0; i < 8; i++)
	{
		if(!mDmaMap[i].bUsed)
		{
			break;
		}
	}
	if(i == 8)
	{
		mSemaphore->Give();
		return(0);
	}
	mDmaMap[i].tDmaChannel->CHCTL = 0;
	mDmaMap[i].tDmaChannel->SAR = tDmaInit->uiSrc;
	mDmaMap[i].tDmaChannel->DAR = tDmaInit->uiDest;
	mDmaMap[i].tDmaChannel->DTCTL_f.BLKSIZE = 1;
	mDmaMap[i].tDmaChannel->CHCTL_f.HSIZE = tDmaInit->tDmaDataType;
	if(tDmaInit->tDmaTransMode == DMA_TRANS_MODE_SFR_TO_MEM)
	{
		mDmaMap[i].tDmaChannel->CHCTL_f.SINC = 0;
		mDmaMap[i].tDmaChannel->CHCTL_f.DINC = 1;
	}
	else if(tDmaInit->tDmaTransMode == DMA_TRANS_MODE_MEM_TO_SFR)
	{
		mDmaMap[i].tDmaChannel->CHCTL_f.SINC = 1;
		mDmaMap[i].tDmaChannel->CHCTL_f.DINC = 0;
	}
	else
	{
		mDmaMap[i].tDmaChannel->CHCTL_f.SINC = 1;
		mDmaMap[i].tDmaChannel->CHCTL_f.DINC = 1;
	}
	if(tDmaInit->tDmaCompleteCallback)
	{
		mDmaMap[i].tDmaCompleteCallback = tDmaInit->tDmaCompleteCallback;
		mDmaMap[i].tDmaChannel->CHCTL_f.IE = 1;
		*(uint32_t *)(((uint32_t)&M4_INTC->SEL0) + (4 * (DMA_IRQ_NUM + i))) = mDmaMap[i].tIrqId;
		NVIC_ClearPendingIRQ((IRQn)(DMA_IRQ_NUM + i));
		NVIC_SetPriority((IRQn)(DMA_IRQ_NUM + i), 4);
		NVIC_EnableIRQ((IRQn)(DMA_IRQ_NUM + i));
	}
	if(mDmaMap[i].tDma->EN_f.EN != 1)
	{
		mDmaMap[i].tDma->EN_f.EN = 1;
		mDmaMap[i].tDma->INTMASK1 = 0x000f0000;
	}
	*mDmaMap[i].uiAos = tDmaInit->usSfrId;
	mDmaMap[i].bUsed = true;
	mDmaMap[i].bCircularMode = tDmaInit->bCircularMode;
	mDmaMap[i].tDmaTransMode = tDmaInit->tDmaTransMode;
	mSemaphore->Give();
	return(i + 1);
}

void dma::StartDmaFsrToMem(HandleTPDF tHandle, uint16_t ucLen, void* pMem)
{
	DmaMapTPDF *ptr;
	
	if(!tHandle || !ucLen || !pMem)
	{
		return;
	}
	ptr = &mDmaMap[tHandle - 1];
	ptr->tDmaChannel->DTCTL_f.CNT = ucLen;
	ptr->tDmaChannel->DAR = (uint32_t)pMem;
	ptr->tDma->INTCLR1 = (1 << (ptr->ucEnIndex + 16 )) | (1 << ptr->ucEnIndex);
	ptr->tDma->CHEN |= 1 << ptr->ucEnIndex;
}

void dma::StartDma(HandleTPDF tHandle, uint16_t ucLen)
{
	DmaMapTPDF *ptr;
	
	if(!tHandle || !ucLen)
	{
		return;
	}
	ptr = &mDmaMap[tHandle - 1];
	ptr->tDmaChannel->DTCTL_f.CNT = ucLen;
	ptr->tDma->INTCLR1 = (1 << (ptr->ucEnIndex + 16 )) | (1 << ptr->ucEnIndex);
	ptr->tDma->CHEN |= 1 << ptr->ucEnIndex;
}

void dma::StartDma(HandleTPDF tHandle, uint16_t ucLen, uint16_t ucMemRptLen, uint16_t ucSfrPptLen, void* pMem)
{	
	uint32_t i;
	DmaMapTPDF *ptr;
	
	if(!tHandle || !ucLen || !pMem)
	{
		return;
	}
	ptr = &mDmaMap[tHandle - 1];
	ptr->tDma->CHEN &= ~(1 << ptr->ucEnIndex);
	switch(ptr->tDmaTransMode)
	{
		case DMA_TRANS_MODE_SFR_TO_MEM:
			if(ptr->bCircularMode)
			{
				ptr->tDmaChannel->DTCTL_f.CNT = 0;
				ptr->tDmaChannel->RPT_f.DRPT = ucLen;
				ptr->tDmaChannel->CHCTL_f.DRPTEN = 1;
			}
			else
			{
				ptr->tDmaChannel->DTCTL_f.CNT = ucLen;
				if(ucLen > ucMemRptLen)
				{
					ptr->tDmaChannel->DTCTL_f.CNT = ucLen / ucMemRptLen;
					ptr->tDmaChannel->RPT_f.DRPT = ucMemRptLen;
					ptr->tDmaChannel->CHCTL_f.DRPTEN = 1;
					ptr->tDmaChannel->DTCTL_f.BLKSIZE = ucMemRptLen;
				}
				else
				{
					ptr->tDmaChannel->RPT_f.DRPT = 0;
					ptr->tDmaChannel->CHCTL_f.DRPTEN = 0;
				}
			}
			ptr->tDmaChannel->DAR = (uint32_t)pMem;
			break;
		case DMA_TRANS_MODE_MEM_TO_SFR:
			if(ptr->bCircularMode)
			{
				ptr->tDmaChannel->DTCTL_f.CNT = 0;
				ptr->tDmaChannel->RPT_f.SRPT = ucLen;
				ptr->tDmaChannel->CHCTL_f.SRPTEN = 1;
			}
			else
			{
				ptr->tDmaChannel->DTCTL_f.CNT = ucLen;
				if(ucLen > ucMemRptLen)
				{
					ptr->tDmaChannel->DTCTL_f.CNT = ucLen / ucMemRptLen;
					ptr->tDmaChannel->RPT_f.SRPT = ucMemRptLen;
					ptr->tDmaChannel->CHCTL_f.SRPTEN = 1;
					ptr->tDmaChannel->DTCTL_f.BLKSIZE = ucMemRptLen;
				}
				else
				{
					ptr->tDmaChannel->RPT_f.SRPT = 0;
					ptr->tDmaChannel->CHCTL_f.SRPTEN = 0;
				}
			}
			ptr->tDmaChannel->SAR = (uint32_t)pMem;
			break;
		default:
			if(ptr->bCircularMode)
			{
				ptr->tDmaChannel->DTCTL_f.CNT = 0;
				ptr->tDmaChannel->RPT_f.DRPT = ucLen;
				ptr->tDmaChannel->CHCTL_f.DRPTEN = 1;
				ptr->tDmaChannel->RPT_f.SRPT = ucSfrPptLen;
				ptr->tDmaChannel->CHCTL_f.SRPTEN = 1;
				ptr->tDmaChannel->DTCTL_f.BLKSIZE = ucSfrPptLen;
			}
			else
			{
				ptr->tDmaChannel->DTCTL_f.CNT = ucLen;
				if(ucLen > ucMemRptLen)
				{
					ptr->tDmaChannel->DTCTL_f.CNT = ucLen / ucMemRptLen;
					ptr->tDmaChannel->RPT_f.DRPT = ucMemRptLen;
					ptr->tDmaChannel->CHCTL_f.DRPTEN = 1;
					ptr->tDmaChannel->DTCTL_f.BLKSIZE = ucMemRptLen;
				}
				else
				{
					ptr->tDmaChannel->RPT_f.DRPT = 0;
					ptr->tDmaChannel->CHCTL_f.DRPTEN = 0;
				}
				if(ucSfrPptLen > 1)
				{
					ptr->tDmaChannel->DTCTL_f.CNT = ucLen / ucSfrPptLen;
					ptr->tDmaChannel->RPT_f.SRPT = ucSfrPptLen;
					ptr->tDmaChannel->CHCTL_f.SRPTEN = 1;
					ptr->tDmaChannel->CHCTL_f.SINC = 1;
					ptr->tDmaChannel->DTCTL_f.BLKSIZE = ucSfrPptLen;
				}
				else
				{
					ptr->tDmaChannel->RPT_f.SRPT = 0;
					ptr->tDmaChannel->CHCTL_f.SRPTEN = 0;
					ptr->tDmaChannel->CHCTL_f.SINC = 0;
				}
			}
			i = ptr->tDmaChannel->SAR;
			ptr->tDmaChannel->SAR = i;
			ptr->tDmaChannel->DAR = (uint32_t)pMem;
			break;
	}
	ptr->tDma->INTCLR1 = (1 << (ptr->ucEnIndex + 16 )) | (1 << ptr->ucEnIndex);
	ptr->tDma->CHEN |= 1 << ptr->ucEnIndex;
}

uint32_t* dma::GetLengthRegister(HandleTPDF tHandle)
{
	if(!tHandle)
	{
		return(0);
	}
	return((uint32_t *)&mDmaMap[tHandle - 1].tDmaChannel->MONDTCTL);
}

void dma::Reset(HandleTPDF tHandle, uint16_t ucLen, uint16_t ucMemRptLen, uint16_t ucSfrPptLen)
{
	volatile uint32_t i;
	DmaMapTPDF *ptr;
	
	if(!tHandle || !ucLen)
	{
		return;
	}
	ptr = &mDmaMap[tHandle - 1];
	ptr->tDma->CHEN &= ~(1 << ptr->ucEnIndex);
	switch(ptr->tDmaTransMode)
	{
		case DMA_TRANS_MODE_SFR_TO_MEM:
			if(ptr->bCircularMode)
			{
				ptr->tDmaChannel->DTCTL_f.CNT = 0;
				ptr->tDmaChannel->RPT_f.DRPT = ucLen;
				ptr->tDmaChannel->CHCTL_f.DRPTEN = 1;
			}
			else
			{
				ptr->tDmaChannel->DTCTL_f.CNT = ucLen;
				if(ucLen > ucMemRptLen)
				{
					ptr->tDmaChannel->DTCTL_f.CNT = ucLen / ucMemRptLen;
					ptr->tDmaChannel->RPT_f.DRPT = ucMemRptLen;
					ptr->tDmaChannel->CHCTL_f.DRPTEN = 1;
					ptr->tDmaChannel->DTCTL_f.BLKSIZE = ucMemRptLen;
				}
				else
				{
					ptr->tDmaChannel->RPT_f.DRPT = 0;
					ptr->tDmaChannel->CHCTL_f.DRPTEN = 0;
				}
			}
			i = ptr->tDmaChannel->DAR;
			ptr->tDmaChannel->DAR = i;
			break;
		case DMA_TRANS_MODE_MEM_TO_SFR:
			if(ptr->bCircularMode)
			{
				ptr->tDmaChannel->DTCTL_f.CNT = 0;
				ptr->tDmaChannel->RPT_f.SRPT = ucLen;
				ptr->tDmaChannel->CHCTL_f.SRPTEN = 1;
			}
			else
			{
				ptr->tDmaChannel->DTCTL_f.CNT = ucLen;
				if(ucLen > ucMemRptLen)
				{
					ptr->tDmaChannel->DTCTL_f.CNT = ucLen / ucMemRptLen;
					ptr->tDmaChannel->RPT_f.SRPT = ucMemRptLen;
					ptr->tDmaChannel->CHCTL_f.SRPTEN = 1;
					ptr->tDmaChannel->DTCTL_f.BLKSIZE = ucMemRptLen;
				}
				else
				{
					ptr->tDmaChannel->RPT_f.SRPT = 0;
					ptr->tDmaChannel->CHCTL_f.SRPTEN = 0;
				}
			}
			i = ptr->tDmaChannel->SAR;
			ptr->tDmaChannel->SAR = i;
			break;
		default:
			if(ptr->bCircularMode)
			{
				ptr->tDmaChannel->DTCTL_f.CNT = 0;
				ptr->tDmaChannel->RPT_f.DRPT = ucLen;
				ptr->tDmaChannel->CHCTL_f.DRPTEN = 1;
				ptr->tDmaChannel->RPT_f.SRPT = ucSfrPptLen;
				ptr->tDmaChannel->CHCTL_f.SRPTEN = 1;
				ptr->tDmaChannel->DTCTL_f.BLKSIZE = ucSfrPptLen;
			}
			else
			{
				ptr->tDmaChannel->DTCTL_f.CNT = ucLen;
				if(ucLen > ucMemRptLen)
				{
					ptr->tDmaChannel->DTCTL_f.CNT = ucLen / ucMemRptLen;
					ptr->tDmaChannel->RPT_f.DRPT = ucMemRptLen;
					ptr->tDmaChannel->CHCTL_f.DRPTEN = 1;
					ptr->tDmaChannel->DTCTL_f.BLKSIZE = ucMemRptLen;
				}
				else
				{
					ptr->tDmaChannel->RPT_f.DRPT = 0;
					ptr->tDmaChannel->CHCTL_f.DRPTEN = 0;
				}
				if(ucSfrPptLen > 1)
				{
					ptr->tDmaChannel->DTCTL_f.CNT = ucLen / ucSfrPptLen;
					ptr->tDmaChannel->RPT_f.SRPT = ucSfrPptLen;
					ptr->tDmaChannel->CHCTL_f.SRPTEN = 1;
					ptr->tDmaChannel->CHCTL_f.SINC = 1;
					ptr->tDmaChannel->DTCTL_f.BLKSIZE = ucSfrPptLen;
				}
				else
				{
					ptr->tDmaChannel->RPT_f.SRPT = 0;
					ptr->tDmaChannel->CHCTL_f.SRPTEN = 0;
					ptr->tDmaChannel->CHCTL_f.SINC = 0;
				}
			}
			i = ptr->tDmaChannel->DAR;
			ptr->tDmaChannel->DAR = i;
			i = ptr->tDmaChannel->SAR;
			ptr->tDmaChannel->SAR = i;
			break;
	}
	ptr->tDma->INTCLR1 = (1 << (ptr->ucEnIndex + 16 )) | (1 << ptr->ucEnIndex);
	ptr->tDma->CHEN |= 1 << ptr->ucEnIndex;
}
